<template>
  <view
    ref="formItemRef"
    class="tn-form-item"
    :class="[itemClass]"
  >
    <!-- 内容容器 -->
    <view
      class="tn-form-item__container"
      :style="containerStyle"
    >
      <!-- label -->
      <view
        v-if="label || $slots.label"
        class="tn-form-item__label"
        :style="labelStyle"
      >
        <slot name="label" :label="label">
          {{ label }}
        </slot>
        <view v-if="required" class="required">*</view>
      </view>

      <!-- 内容 -->
      <view class="tn-form-item__content">
        <slot></slot>
      </view>
    </view>
    
    <!-- 校验信息 -->
    <view
      v-if="showError('message')"
      class="tn-form-item__error-message"
      :style="messageStyle"
    >
      {{ validateMessage }}
    </view>
  </view>
</template>

<script lang="ts" setup>
import { 
  formItemProps, 
  FormContext, 
  FormItemContext, 
  formContextKey, 
  formItemContextKey, 
  FormLabelPosition, 
  FormLabelAlign, 
  FormErrorType, 
  FormItemValidateState, 
  FormItemRule, 
  FormValidateFailure, 
  FormErrorTypeArray
} from '../../../types/tn-form'
import { 
  computed, 
  CSSProperties, 
  inject, 
  nextTick, 
  onBeforeUnmount, 
  onMounted, 
  provide, 
  reactive, 
  ref, 
  toRefs, 
  useSlots 
} from 'vue'
import { getProp, handleValueWithUnit, Arrayable } from '../../../utils'
import { showValidateError } from '../lib/utils'
// import { RuleItem } from 'async-validator'
// import AsyncValidator from 'async-validator'
import { RuleItem } from '../../../utils/async-validate'
import AsyncValidator from '../../../utils/async-validate'
import { castArray, isEqual, clone } from 'lodash-unified'
import { isFunction } from '@vue/shared'

const props = defineProps(formItemProps)
const slots = useSlots()

// el
const formItemRef = ref(null)

// formContext
const formContext: FormContext | undefined = inject(formContextKey, undefined)

const borderBottom = computed<boolean>(() => {
  return props?.borderBottom !== undefined ? (props?.borderBottom || false) : (formContext?.borderBottom || false)
})
const labelPosition = computed<FormLabelPosition>(() => {
  return (props?.labelPosition || formContext?.labelPosition || 'left') as FormLabelPosition
})
const labelWidth = computed<string>(() => {
  return handleValueWithUnit(props?.labelWidth || formContext?.labelWidth || 90)
})
const labelAlign = computed<FormLabelAlign>(() => {
  return (props?.labelAlign || formContext?.labelAlign || 'left') as FormLabelAlign
})
// container样式
const containerStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (labelPosition.value === 'top') {
    style.flexDirection = 'column'
    style.padding = '0rpx 10rpx'
  } else if (labelPosition.value === 'left') {
    style.flexDirection = 'row'
    style.alignItems = 'center'
  }
  return style
})
// label样式
const labelStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (labelPosition.value === 'left') {
    if (labelWidth.value) {
      style.width = labelWidth.value
      style.flex = `0 0 ${labelWidth.value}`
    }
  } else if (labelPosition.value === 'top') {
    style.marginBottom = '10rpx'
  }
  
  // 判断label对齐方式
  if (labelAlign.value === 'left') {
    style.justifyContent = 'flex-start'
  } else if (labelAlign.value === 'center') {
    style.justifyContent = 'center'
  } else if (labelAlign.value === 'right') {
    style.justifyContent = 'flex-end'
  }
  return Object.assign(style, props?.labelStyle || {})
})
// 校验提示信息样式
const messageStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (labelPosition.value === 'left') {
    style.marginLeft = labelWidth.value
  } else if (labelPosition.value === 'top') {
    style.marginLeft = '10rpx'
  }
  return style
})

// item对应的类
const itemClass = computed<string>(() => {
  let clazz: string[] = []
  if (borderBottom.value) {
    clazz.push('tn-border-bottom')
    if (showError('borderBottom')) {
      clazz.push('tn-form-item--error-bottom')
    }
  }
  return clazz.join(' ')
})

// 获取model数据
const fieldValue = computed(() => {
  const model = formContext?.model
  if (!model || !props.prop) return
  return getProp(model, props.prop).value
})
// 获取校验规则
const _rules = computed<FormItemRule[]>(() => {
  let rules: FormItemRule[] = []
  const formRules = formContext?.rules
  if (formRules && Object.keys(formRules).length > 0 && props.prop) {
    const _rules = getProp<Arrayable<FormItemRule> | undefined>(formRules, props.prop).value
    if (_rules) {
      rules.push(...castArray(_rules))
    }
  }

  if (props.required !== undefined) {
    rules.push({ required: !!props.required })
  }
  return rules
})
// 是否开启校验
const validateEnabled = computed<boolean>(() => _rules.value.length > 0)
// 获取对应事件的校验规则
const getFilteredRule = (trigger: string): RuleItem[] => {
  const rules = _rules.value
  return (
    rules
      .filter((rule) => {
        if (!rule.trigger || !trigger) return true
        if(Array.isArray(rule.trigger)) {
          return rule.trigger.includes(trigger)
        } else {
          return rule.trigger === trigger
        }
      })
      .map(({ trigger, ...rule }): RuleItem => rule)
  )
}

// 验证结果
let validateState = ref<FormItemValidateState>('')
// 校验信息
let validateMessage = ref<string>('')
// 重置校验规则状态
let isResettingField: boolean = false
// 初始化时的model数据
let initialModelValue: any = undefined

// 判断是否为对应的错误提示类型
const showError = (type: FormErrorType) => {
  return showValidateError(formContext?.errorType as FormErrorTypeArray, validateState.value, type)
}
// 设置验证状态
const setValidateState = (state: FormItemValidateState) => {
  validateState.value = state
}

// 校验失败事件
const onValidationFailed = (error: FormValidateFailure) => {
  const { errors, fields } = error
  if (!errors || !fields) {
    console.error(error)
  }

  setValidateState('error')
  validateMessage.value = errors
    ? errors?.[0]?.message ?? `${props.prop}为必填项`
    : ''
  
  // 判断是否有toast
  if (showError('toast')) {
    uni.showToast({
      title: validateMessage.value,
      icon: 'none'
    })
  }
}
// 校验通过事件
const onValidationSuccess = () => {
  setValidateState('success')
}

// 校验操作
const doValidate = async (rules: RuleItem[]): Promise<true> => {
  const modelName = props.prop
  const validator = new AsyncValidator({
    [modelName]: rules
  })
  return validator
    .validate({ [modelName]: fieldValue.value }, { firstFields: true })
    .then(() => {
      onValidationSuccess()
      return true as const
    })
    .catch((err: FormValidateFailure) => {
      onValidationFailed(err as FormValidateFailure)
      return Promise.reject(err)
    })
}

// 对该表单项进行重置，将其值重置为初始值并移除校验结果
const resetField: FormItemContext['resetField'] = async () => {
  const model = formContext?.model
  if (!model || !props.prop) return

  const computedValue = getProp(model, props.prop)
  if (!isEqual(computedValue.value, initialModelValue)) {
    // 如果现在设置的规则和初始化时的规则不一样时触发重置
    isResettingField = true
  }
  computedValue.value = initialModelValue
  await nextTick()
  clearValidate()
}

// 移除该表单项的校验结果
const clearValidate: FormItemContext['clearValidate'] = () => {
  setValidateState('')
  validateMessage.value = ''
}

// 对标单项进行校验
const validate: FormItemContext['validate'] = async(trigger, callback) => {
  if (isResettingField) {
    isResettingField = false
    return false
  }

  if (!validateEnabled.value) {
    callback?.(false)
    return false
  }

  const rules = getFilteredRule(trigger)
  if(rules.length === 0) {
    callback?.(true)
    return true
  }
  setValidateState('validating')
  return doValidate(rules)
    .then(() => {
      callback?.(true)
      return true as const
    })
    .catch((err: FormValidateFailure) => {
      const { fields } = err
      callback?.(false, fields)
      return isFunction(callback) ? false : Promise.reject(fields)
    })
}

// formItemContext
const context: FormItemContext = reactive({
  ...toRefs(props),
  $el: formItemRef,
  validateState,
  resetField,
  clearValidate,
  validate
})

provide(formItemContextKey, context)

onMounted(() => {
  // 如果存在prop参数才进行添加到form中
  if (props?.prop) {
    formContext?.addField(context)
    initialModelValue = clone(fieldValue.value)
  }
})

onBeforeUnmount(() => {
  formContext?.removeField(context)
})
</script>

<script lang="ts">
export default {
  options: {
    // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
    virtualHost: true
  }
}
</script>

<style lang="scss" scoped>
@import '../../../theme.scss';
.tn-form-item {
  width: 100%;
  display: flex;
  flex-direction: column;
  padding: 20rpx 10rpx;
  padding-bottom: 0rpx;
  font-size: $tn-font-size;

  &--error-bottom {
    border-color: map-get(map-get($tn-type-color-map, 'danger'), 'normal');
  }

  &__container {
    display: flex;
    width: 100%;
    margin-bottom: 10rpx;
  }
  &__label {
    height: 100%;
    display: flex;
    align-items: center;
  }
  .required {
    color: map-get(map-get($tn-type-color-map, 'danger'), 'normal');
    transform: scale(0.9);
    transform-origin: top left;
  }

  &__content {
    flex: 1;
  }

  &__error-message {
    color: map-get(map-get($tn-type-color-map, 'danger'), 'normal');
    transform: scale(0.8);
    transform-origin: top left;
  }
}
</style>
